#if BACKEND
using File = MultiAgentsServer.Scripts.Services.FileOperationService;
#elif FRONTEND
using File = MultiAgentsClient.Tools.FileOperationToolProvider;
#endif
using System.Diagnostics;
using MultiAgentsServer.Scripts.Shared;

namespace MultiAgentsShared;

public class Debug
{
#if FRONTEND
    private static ILogger<Debug> _logger;

    public record DisplaySystemMessageCommand(string Message);
#endif
    private static readonly object consoleLock = new();
    private static readonly object fileLock = new();

    public static void Init(ILogger<Debug> logger)
    {
#if FRONTEND
        _logger = logger;
#endif
    }

    public static void LogLine(string message, ConsoleColor color = ConsoleColor.White, bool withTime = true)
    {
        lock (consoleLock)
        { // Ensure only one thread can execute this block at a time
            if (withTime)
            {
                message = ConvertLogString(message);
#if BACKEND
                Console.ForegroundColor = color;
                Console.WriteLine(message);
                Console.ResetColor();
#elif FRONTEND
                _logger.LogInformation(message);
#endif
                LogLocal(message);
            }
            else
            {
#if BACKEND
                Console.ForegroundColor = color;
                Console.WriteLine($"{message}");
                Console.ResetColor();
#elif FRONTEND
                _logger.LogInformation($"{message}");
#endif
                LogLocal($"{message}\r");
            }
        }
    }

    public static void Log(string message, ConsoleColor color = ConsoleColor.White, bool withTime = true)
    {
        lock (consoleLock)
        { // Ensure only one thread can execute this block at a time
            if (withTime)
            {
                message = ConvertLogString(message);
#if BACKEND
                Console.ForegroundColor = color;
                Console.Write(message);
                Console.ResetColor();
#elif FRONTEND
                _logger.LogInformation(message);
#endif
                LogLocal(message);
            }
            else
            {
#if BACKEND
                Console.ForegroundColor = color;
                Console.Write($"{message}");
                Console.ResetColor();
#elif FRONTEND
                _logger.LogInformation($"{message}");
#endif
                LogLocal($"{message}\r");
            }
        }
    }

    public static void LogError(string message)
    {
        lock (consoleLock)
        { // Ensure only one thread can execute this block at a time
            message = ConvertLogString(message);
#if BACKEND
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(message);
            Console.ResetColor();
#elif FRONTEND
            _logger.LogError(message);
#endif
            LogLocal(message);
        }
    }

    public static void LogWarning(string message)
    {
        lock (consoleLock)
        { // Ensure only one thread can execute this block at a time
            message = ConvertLogString(message);
#if BACKEND
            Console.ForegroundColor = ConsoleColor.DarkYellow;
            Console.WriteLine(message);
            Console.ResetColor();
#elif FRONTEND
            _logger.LogWarning(message);
#endif
            LogLocal(message);
        }
    }

#if FRONTEND
    public static void LogWindow(string message)
    {
        lock (consoleLock)
        { // Ensure only one thread can execute this block at a time
            EventBus eventBus = EventBus.Instance;
            eventBus.Publish(new DisplaySystemMessageCommand(message));
        }
    }
#endif

    private static void LogLocal(string message)
    {
        lock (fileLock)
        {
            string documentsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            string logFileName = Path.Combine(documentsDirectory, FileConsts.LogFileName);

            if (File.CheckFileExist(logFileName) == false)
            {
                File.CreateFile(logFileName, message);
            }
            else
            {
                string content = File.LoadFile(logFileName);
                File.CreateFile(logFileName, content + message);
            }
        }
    }

    public static InvalidOperationException ShouldNotHappen()
    {
        StackTrace stackTrace = new(true);
        var stackFrames = stackTrace.GetFrames();

        if (stackFrames == null) return new InvalidOperationException();

        string stackInfo = "Unexpected call in the code. Call stack:\n";

        foreach (StackFrame frame in stackFrames)
        {
            stackInfo += $"{frame.GetMethod()?.DeclaringType?.FullName}.{frame.GetMethod()?.Name} " +
                         $"in {frame.GetFileName()}:line {frame.GetFileLineNumber()}\n";
        }

        if (Debugger.IsAttached)
        {
#if LOCALDEV
            Debugger.Break();
#endif
        }

        return new InvalidOperationException(stackInfo);
    }

    private static string ConvertLogString(string message)
    {
        StackTrace stackTrace = new(true);
        StackFrame? frame = stackTrace.GetFrame(2);

        string callerClassName = frame?.GetMethod()?.DeclaringType?.Name ?? "";
        int lineNumber = frame?.GetFileLineNumber() ?? 0;

        return $"{DateTime.Now} {callerClassName} {lineNumber} line:\n>> {message}";
    }
}
